package rpc.aop;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import javafx.util.Pair;
import lombok.SneakyThrows;
import rpc.annotation.TargetServiceName;
import rpc.common.NodeInformation;
import rpc.common.TaskRpc;
import rpc.init.RpcConfig;
import rpc.units.SocketUnits;

import java.io.IOException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;

public class XiaobaibaiClientProxy<T> implements InvocationHandler {

    //根据serverName获取节点信息
    private Map<String, Pair<AtomicInteger, List<NodeInformation>>> nodeInformationMap=new HashMap<>();

    //代理目标接口
    private Class<T> interfaces;

    public XiaobaibaiClientProxy(Class<T> interfaces) {
        this.interfaces = interfaces;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        TargetServiceName annotation = interfaces.getDeclaredAnnotation(TargetServiceName.class);

        String serverName = annotation.name();

        Class[] paramsClass=new Class[args.length];
        for (int i = 0; i < args.length; i++) {
            paramsClass[i]=args[i].getClass();
        }

        TaskRpc taskRpc=new TaskRpc(serverName,annotation.classify(),method.getName(),args,paramsClass);

        Pair<AtomicInteger, List<NodeInformation>> pair = nodeInformationMap.get(serverName);
        if(pair==null){
            //去注册中心获取信息
            String nodeStr=this.callService(taskRpc);
            HashSet<JSONObject> nodeInformationSet = JSON.parseObject(nodeStr, HashSet.class);
            List<NodeInformation> list = new ArrayList<>();
            for (JSONObject jsonObject : nodeInformationSet) {
                NodeInformation nodeInformation = JSON.parseObject(JSON.toJSONString(jsonObject), NodeInformation.class);
                list.add(nodeInformation);
            }
            pair=new Pair<>(new AtomicInteger(), list);
            nodeInformationMap.put(serverName,pair);
        }

        NodeInformation nodeInformation=getRandomNode(pair);

        //去调用服务端
        String result = this.callServer(nodeInformation,JSON.toJSONString(taskRpc));

        Class<?> returnType = method.getReturnType();

        return JSON.parseObject(result,returnType);

    }

    private String callService(TaskRpc taskRpc) throws IOException {
        Socket socket = new Socket(RpcConfig.registryCenterIp, RpcConfig.registryCenterPort);
        SocketUnits.sendSuccessMsg(socket, JSON.toJSONString(taskRpc));
        return SocketUnits.receiveMsg(socket);
    }

    @SneakyThrows
    private String callServer(NodeInformation nodeInformation, String msg) {
        Socket socket=new Socket();
        SocketAddress socketAddress=new InetSocketAddress(nodeInformation.getIp(),nodeInformation.getPort());
        socket.connect(socketAddress);
        SocketUnits.sendSuccessMsg(socket,msg);
        String result = SocketUnits.receiveMsg(socket);
        this.close(socket);
        return result;
    }

    //负载均衡
    private NodeInformation getRandomNode(Pair<AtomicInteger,List<NodeInformation>> pair){
        //使用迭代器,可能会出现线程安全的问题

        List<NodeInformation> list = pair.getValue();

        int count = pair.getKey().getAndIncrement();

        if(count==Integer.MAX_VALUE-1000){
            pair.getKey().set(1);
        }

        return list.get(count%list.size());
    }

    private void close(Socket socket) throws IOException {
        socket.close();
    }

}
